using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;

namespace Zsa.Reflection.Discovery
{
    /// <summary>
    /// Search an assembly on group of assemblies for 
    /// types.
    /// </summary>
    public abstract class SearchBase
    {
        #region Delegates
        /// <summary>
        /// Delegate used internal when a type is found.
        /// </summary>
        /// <param name="type">The type.</param>
        /// <returns>when value is true continues searching.</returns>
        public delegate bool TypeFoundHandler(Type[] type, object state);

        /// <summary>
        /// Delegate used when a member is found.
        /// </summary>
        /// <param name="memberInfo">The member info.</param>
        /// <param name="state">The state.</param>
        /// <returns></returns>
        public delegate bool MemberFoundHandler(MemberInfo[] memberInfo, object state);

        /// <summary>
        /// Delegate used when attribute data is found.
        /// </summary>
        /// <param name="memberInfo"></param>
        /// <param name="state"></param>
        /// <returns></returns>
        public delegate bool AttributeFoundHandler(IList<CustomAttributeData> attributeData, object state);
        #endregion Delegates
        
        #region Constructor
        /// <summary>
        /// Initializes a new instance of the <see cref="TypeSearch"/> class.
        /// </summary>
        /// <param name="criteria">The criteria.</param>
        protected internal SearchBase()
        {
        }
        #endregion Constructor
        

        #region Methods
        /// <summary>
        /// Finds the specified locate action.
        /// </summary>
        /// <param name="locateAction">The locate action.</param>
        /// <param name="assembly">The assembly.</param>
        protected virtual void FindTypes(TypeFoundHandler typeFoundAction, ITypeFilter typeFilter,
            Assembly assembly, object state)
        {
            // Check all the arguments.
            if (typeFoundAction == null)
            {
                throw new ArgumentNullException("locateAction", "The locate action argument cannot be null !");
            }
            if (assembly == null)
            {
                throw new ArgumentNullException("assembly", "The assembly argument cannot be null !");
            }

            TypeFilter _TypeFilter = new TypeFilter(typeFilter.CheckType);
            object _Filter = typeFilter.Filter;

            // Get the modules for the assembly.  Exclude resource modules.
            Module[] _Modules = assembly.GetModules(false);
            // Find the types.
            Type[] _Types = null;
            for (int _ModuleIndex = 0; _ModuleIndex < _Modules.Length; _ModuleIndex++)
            {
                _Types = _Modules[_ModuleIndex].FindTypes(_TypeFilter, _Filter);
                typeFoundAction(_Types, state);
            }
        }

        protected virtual void Find(Assembly[] assemblies,
            TypeFoundHandler typeFoundHandler, ITypeFilter typeFilter,
            MemberFoundHandler memberFoundHandler, IMemberFilter memberFilter)
        {
        }


        protected void FindTypes(Assembly assembly, MemberFoundHandler attributeFoundHandler,
            IMemberFilter attributeFilter, object state)
        {
        }

        protected void FindMembers(Assembly assembly, MemberFoundHandler attributeFoundHandler,
            IMemberFilter attributeFilter, object state)
        {
        }

        protected void FindAttributes(Assembly assembly, TypeFoundHandler attributeFoundHandler, 
            IAttributeFilter attributeFilter, object state )
        {
        }
        

        protected void Find( Assembly[] assemblies,
            TypeFoundHandler typeFoundHandler, ITypeFilter typeFilter, 
            MemberFoundHandler memberFoundHandler, IMemberFilter memberFilter, 
            AttributeFoundHandler attributeFoundHandler, IAttributeFilter attributeFilter,
            object state)
        {
            // Check all the arguments.
            if (assemblies == null)
            {
                throw new ArgumentNullException("assemblies", "The assembly argument cannot be null !");
            }
            if (assemblies.Length < 1)
            {
                throw new ArgumentException("There must be at least one assembly !");
            }

            // Check the delegates
            
            // Temp variables
            Module[] _Modules = null;
            Type[] _Types = null;
            MemberInfo[] _Members = null;
            IList<CustomAttributeData> _AttributeData = null;

            bool _CheckTypes =
                memberFoundHandler != null || (attributeFilter != null &&
                (attributeFilter.SearchType == SearchType.Member ||
                    attributeFilter.SearchType == SearchType.Type));

            bool _CheckAttributes = 
                attributeFilter.SearchType == SearchType.Attribute &&
                attributeFoundHandler != null &&
                attributeFilter != null ;

                
                


            // Loop through the assemblies.
            for (int _AsmIndex = 0; _AsmIndex < assemblies.Length; _AsmIndex++)
            {

                // Check the attributes.
                if (_CheckAttributes)
                {
                   
                    _AttributeData = CustomAttributeData.GetCustomAttributes(assemblies[_AsmIndex]);
                    attributeFoundHandler(_AttributeData,state);

                }

                // Get the modules.
                _Modules = assemblies[_AsmIndex].GetModules(false);
                // Loop through the modules.
                for (int _ModuleIndex = 0; _ModuleIndex < _Modules.Length; _ModuleIndex++)
                {




                    // If the type filter is null then get all types.
                    _Types = typeFilter == null ?
                        _Modules[_ModuleIndex].GetTypes() :
                        _Modules[_ModuleIndex].FindTypes(
                            new TypeFilter(typeFilter.CheckType),
                            typeFilter.Filter
                    );

                    // If the type found handler is not null then invoke the delegate.
                    if (typeFoundHandler != null)
                    {
                        typeFoundHandler(_Types,state);
                    }

                    if (!_CheckTypes)
                    {
                        continue;
                    }



                    // if the member found handler is not null or the attrbiute 
                    if (attributeFoundHandler != null || attributeFoundHandler != null &&
                        (attributeFilter != null && (attributeFilter.SearchType == SearchType.Member || attributeFilter.SearchType == SearchType.Type))
                        )
                    {
                        // Loop through the types.
                        for (int _TypeIndex = 0; _TypeIndex < _Types.Length; _TypeIndex++)
                        {
                            if ( attributeFilter != null && attributeFilter.SearchType == SearchType.Type )
                            {
                                _AttributeData = CustomAttributeData.GetCustomAttributes(_Types[_TypeIndex]);

                            }

                            // Get the members using the member filter.
                            _Members = memberFilter == null ? 
                                _Types[_TypeIndex].GetMembers() :
                                _Types[_TypeIndex].FindMembers(
                                    memberFilter.MemberTypes,
                                    memberFilter.BindingFlags,
                                    new MemberFilter(memberFilter.CheckMember),
                                    memberFilter.Filter
                                );

                            if (attributeFilter != null && attributeFilter.SearchType == SearchType.Member)
                            {
                                //_AttributeData = CustomAttributeData.GetCustomAttributes(_Types[_TypeIndex]);
                                //_AttributeData = attributeFilter.Filter == null ?
                                //    _Types[_TypeIndex].GetCustomAttributes(attributeFilter.Inherit) :
                                //    _Types[_TypeIndex].GetCustomAttributes(attributeFilter.Inherit, attributeFilter.AttributeType);

                            }

                            // If the delegate is not null the invoke.
                            if (memberFoundHandler != null)
                            {
                                memberFoundHandler(_Members, state);
                            }

                            if ( attributeFilter != null && attributeFilter.SearchType == SearchType.Member )
                            {

                            }
                        }
                    }


                    // If the member filter is not null then loop through members
                    if (memberFilter != null)
                    {
                        for (int _TypeIndex = 0; _TypeIndex < _Types.Length; _TypeIndex++)
                        {
                            // Get the members using the member filter.
                            _Members = _Types[_TypeIndex].FindMembers(
                                memberFilter.MemberTypes,
                                memberFilter.BindingFlags,
                                new MemberFilter(memberFilter.CheckMember),
                                memberFilter.Filter
                            );

                            // If the delegate is not null the invoke.
                            if (memberFoundHandler != null)
                            {
                                memberFoundHandler(_Members, state);
                            }
                        }
                    }
                }

            }

         }


        #endregion Methods
    }
}
